﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using HtmlAgilityPack;
using PMC.Plugins;
using PMC.Scripts.Default;
using PMC.Structs;
using HtmlDocument = HtmlAgilityPack.HtmlDocument;

namespace PMC.Urls.Default
{
    //Bug no websites gives problem
    [ScriptInfo("Online Inbox Checker", "Martijn Lentink", "Checkt online inbox van de opgegevens websites voor te verwerken URLs.", "1.0", "nl")]
    [ScriptInfo("Online Inbox Checker", "Martijn Lentink", "Checks online inbox' of the websites for URLs to process.", "1.0", "en")]
    [DefaultScript]
    public class OnlineInboxChecker : UriCollector
    {
        private readonly string settingsPath = Properties.Settings.Default.SettingsFolder + "PMC.UriCollector.OnlineInboxChecker.xml";

        private readonly string[] relativeUrls = new[] {"/pages/inbox.php", "/inbox.php"};
        private bool deleteAfterReading = true;
        private readonly List<string> removeList = new List<string>();

        #region Overrides of UriCollector

        public override void Update()
        {
            Process();
        }

        public override Task UpdateAsync()
        {
            return Task.Factory.StartNew(Process);
        }

        public override DialogResult ShowSettings()
        {
            using (var settings = new InboxCheckerSettings())
            {
                settings.Delete = deleteAfterReading;
                var result = settings.ShowDialog();
                deleteAfterReading = settings.Delete;
                changed = true;
                return result;
            }
        }

        private bool changed;
        public override bool Changed
        {
            get { return changed; }
            protected set { changed = value; }
        }

        public override void LoadSettings()
        {
            try
            {
                deleteAfterReading = Global.XMLDeserialize<bool>(settingsPath);
                changed = false;
            }
            catch (Exception e)
            {
                Log.Instance.AddException(e);
                throw;
            }
        }

        public override void SaveSettings()
        {
            try
            {
                Global.XMLSerialize(settingsPath, deleteAfterReading);
                changed = false;
            }
            catch (Exception e)
            {
                Log.Instance.AddException(e);
                throw;
            }
        }

        public override void Reload()
        {
            Load();
        }

        #endregion

        private void Process()
        {
            //Loops through websites in list
            foreach (var website in Website.Websites)
            {
                HtmlDocument document = null;
                string inboxUrl = string.Empty;
                foreach (string relativeUrl in relativeUrls)
                {
                    try
                    {
                        inboxUrl = website.Webpage + relativeUrl;
                        document = GetWebpage(inboxUrl, website);
                        if (document != null)
                            break;
                    }
                    catch (WebException)
                    {
                    }
                }

                if (document == null)
                    continue;


                var inbox = new List<string>();
                Log.Instance.AddMessage("Checking " + website.Name + " inbox.");

                if (deleteAfterReading)
                {
                    //Gets the Id's to remove afterwards
                    foreach (HtmlNode input in document.DocumentNode.Descendants("input"))
                    {
                        var name = input.GetAttributeValue("name", string.Empty);

                        if (name.IndexOf("inbox_msg", StringComparison.OrdinalIgnoreCase) > -1)
                        {
                            removeList.Add(name);
                        }
                    }
                }

                //Loops through all messages in the inbox for links to process
                foreach (var link in document.DocumentNode.Descendants("a"))
                {
                    string url;
                    try
                    {
                        url = link.GetAttributeValue("href", string.Empty);
                    }
                    catch (UnauthorizedAccessException) //This happens on some links for an unknown reason, maybe some links just dont have an 'href'-tag and this throws an error
                    {
                        Log.Instance.AddMessage("Unable to get 'href'-attribute...", LogType.Warning);
                        continue;
                    }

                    if (url.IndexOf("scripts/runner.php?IM=", StringComparison.OrdinalIgnoreCase) == -1)
                        continue;

                    inbox.Add(url);
                }

                //Loops through every message from the inbox
                foreach (var link in inbox)
                {
                    HtmlDocument doc = Global.HtmlUtility.HttpPageRequest(new Uri(link));
                    try
                    {
                        foreach (var url in doc.DocumentNode.Descendants("a"))
                        {
                            string uri;
                            try
                            {
                                uri = url.GetAttributeValue("href", string.Empty);
                            }
                            catch (UnauthorizedAccessException)
                            {
                                Log.Instance.AddMessage("Unable to get 'href'-attribute...", LogType.Warning);
                                continue;
                            }
                            if (!Uri.IsWellFormedUriString(uri, UriKind.Absolute))
                                continue;

                            if (UrlExpressions.Instance.TestString(uri)) //Tests string for expressions
                            {
                                var mailUrl = new Uri(uri);
                                Add(mailUrl);
                                //OnNext(); //Notifies observers for newly added links
                            }
                        }
                    }
                    catch (ArgumentOutOfRangeException exception)
                    {
                        Log.Instance.AddException(exception);
                    }
                }
                if (deleteAfterReading) //If 'Delete after reading' is enabled clear inbox
                    ClearInbox(inboxUrl, inbox);
            }
            Log.Instance.AddMessage("Finish processing inbox");
            OnCompleted();
        }

        /// <summary>
        /// Clears inbox on the website
        /// </summary>
        /// <param name="website">Website url (containing inbox-url)</param>
        /// <param name="mails">List of message-urls</param>
        private void ClearInbox(string website, List<string> mails)
        {
            if (mails.Count == 0)
                return;

            Log.Instance.AddMessage("Clearing inbox");

            //const string postFormat = "inbox_msg%5B{0}%5D=1"; //POSTs format is as follows: 'inbox_msg[....]' where the last four characters of the message fills in the blank.
            string postData = "";

            removeList.ForEach(str => postData += str.Replace("[", "%5B").Replace("]", "%5D") + "=1&");
            //Lets fill those blanks with the message IDs
            //foreach (var url in mails)
            //{
            //    var uri = url.Replace("/", "");
            //    if (uri.IndexOf("&PHPSESSID", StringComparison.OrdinalIgnoreCase) > -1)
            //        uri = uri.Substring(0, uri.IndexOf("&PHPSESSID", StringComparison.OrdinalIgnoreCase));
            //    var emailUID = uri.Substring(uri.Length - 5);
            //    postData += string.Format(postFormat, emailUID) + "&"; //Concates all the messages together
            //}
            if (mails.Count > 1)
                postData = postData.Substring(0, postData.Length - 1); //Last '&' not necessery, so we'll remove it
            Global.HtmlUtility.HttpPageRequest(new Uri(website), "POST", postData); //Post the data to the page
        }

        /// <summary>
        /// Creates webrequest
        /// </summary>
        /// <param name="url">Url for webrequest</param>
        /// <param name="method">GET or POST</param>
        /// <param name="postData">Data to post</param>
        /// <returns>Document from the request</returns>
        private HtmlDocument GetWebpage(string url, Website website, string method = "GET", string postData = "")
        {
            website.Login();
            return Global.HtmlUtility.HttpPageRequest(new Uri(url), method, postData);
        }
    }
}
